#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
run_perf_tests.py  （放在 scripts/collect/ 下）
批量执行 Homecheck + Hapray，并将四个测量文件输出到：
  artifacts/test_run_YYYYMMDD_HHMMSS/ 下（固定文件名）：
    • fileDepGraph.json
    • moduleDepGraph.json
    • hapray_report.json
    • component_timings.json
依赖的配置文件：
  configs/config.json  （由 configs/config.example.json 拷贝填写）
"""

import os
import sys
import json
import shutil
import argparse
import datetime
import subprocess
from typing import List, Dict, Any


# --------------------------------------------------------------------------- #
# 工具函数
# --------------------------------------------------------------------------- #
def run_command(command: List[str] | str,
                cwd: str | None = None,
                shell: bool = False,
                check_result: bool = True,
                error_message: str = "命令执行失败",
                env_vars: Dict[str, str] | None = None,
                live_output: bool = False) -> subprocess.CompletedProcess:
    """执行系统命令并处理输出 / 错误。"""
    print(f"\n>>> 执行命令: {command if isinstance(command, str) else ' '.join(command)}")
    if cwd:
        print(f"    工作目录: {cwd}")

    full_env = os.environ.copy()
    if env_vars:
        full_env.update(env_vars)
        diff = {k: v for k, v in env_vars.items() if os.environ.get(k) != v}
        if diff:
            print(f"    追加环境变量: {diff}")

    stdout_dest = None if live_output else subprocess.PIPE
    stderr_dest = None if live_output else subprocess.PIPE
    if live_output:
        print("--- 实时输出开始 ---")

    try:
        result = subprocess.run(
            command, cwd=cwd, shell=shell, env=full_env,
            text=True, encoding="utf-8", errors="replace",
            stdout=stdout_dest, stderr=stderr_dest, check=False
        )
    finally:
        if live_output:
            print("--- 实时输出结束 ---")

    if not live_output:
        if result.stdout:
            print("[STDOUT]\n", result.stdout)
        if result.stderr:
            print("[STDERR]\n", result.stderr)

    if check_result and result.returncode != 0:
        print(f"{error_message}（退出码 {result.returncode}）")
        sys.exit(1)

    return result


def ensure_exists(path: str, kind: str):
    if not os.path.exists(path):
        sys.exit(f"错误：{kind} 不存在：{path}")


# --------------------------------------------------------------------------- #
# 主流程
# --------------------------------------------------------------------------- #
def main() -> None:
    # 1) CLI
    parser = argparse.ArgumentParser(
        description="执行 Homecheck + Hapray 并导出四个测量文件")
    parser.add_argument("TestCaseRegex", type=str,
                        help='Hapray 测试用例正则，例如 ".*" 或具体包名')
    args = parser.parse_args()

    # 2) 读取配置（从仓库根的 configs/config.json）
    script_dir = os.path.dirname(os.path.abspath(__file__))
    repo_root = os.path.abspath(os.path.join(script_dir, "..", ".."))
    cfg_path = os.path.join(repo_root, "configs", "config.json")
    ensure_exists(cfg_path, "配置文件 configs/config.json")

    with open(cfg_path, "r", encoding="utf-8") as f:
        cfg = json.load(f)

    homecheck_path: str = cfg.get("homecheck_path", "")
    hapray_root:   str = cfg.get("hapray_path", "")
    output_root:   str = os.path.join(repo_root, "artifacts")  # 固定到仓库 artifacts
    os.makedirs(output_root, exist_ok=True)

    for key, val in (("homecheck_path", homecheck_path),
                     ("hapray_path",   hapray_root)):
        if not val:
            sys.exit(f"配置缺失: {key}")

    # 3) 本次运行输出目录（仓库内）
    timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
    run_dir = os.path.join(output_root, f"test_run_{timestamp}")
    os.makedirs(run_dir, exist_ok=True)
    print(f"\n>>> 输出目录: {run_dir}")

    # 4) Homecheck
    print("\n=== Homecheck ===")
    hc_tmp_dir = os.path.join(run_dir, "homecheck_tmp")
    os.makedirs(hc_tmp_dir, exist_ok=True)

    tool_js = os.path.join(homecheck_path, "node_modules", "homecheck", "lib", "tools", "toolRun.js")
    proj_cfg = os.path.join(homecheck_path, "config", "projectConfig.json")
    ensure_exists(tool_js, "Homecheck toolRun.js")
    ensure_exists(proj_cfg, "Homecheck projectConfig.json")

    hc_cmd = [
        "node", "--max-old-space-size=2048",
        tool_js,
        f"--projectConfigPath={proj_cfg}",
        f"--depGraphOutputDir={hc_tmp_dir}"
    ]
    run_command(hc_cmd, cwd=homecheck_path, error_message="Homecheck 执行失败")

    # 复制两个依赖图（固定文件名到 run_dir 根）
    for fname in ("fileDepGraph.json", "moduleDepGraph.json"):
        src = os.path.join(hc_tmp_dir, fname)
        dst = os.path.join(run_dir, fname)
        if os.path.exists(src):
            shutil.copy(src, dst)
            print(f"  √ 复制 {fname}")
        else:
            print(f"  × 缺失 {fname}")

    # 5) Hapray
    print("\n=== Hapray ===")
    hap_exec = os.path.join(hapray_root, "perf_testing")
    # 兼容 Windows/Linux 的 venv 结构
    hap_py_win = os.path.join(hap_exec, ".venv", "Scripts", "python.exe")
    hap_py_nix = os.path.join(hap_exec, ".venv", "bin", "python")
    hap_py = hap_py_win if os.path.exists(hap_py_win) else hap_py_nix
    ensure_exists(hap_py, "Hapray 虚拟环境 Python")

    reports_root = os.path.join(hap_exec, "reports")

    hap_env = os.environ.copy()
    hap_env["PYTHONPATH"] = hapray_root + (";" if os.name == "nt" else ":") + hap_env.get("PYTHONPATH", "")

    run_command(
        [hap_py, "-m", "scripts.main", "perf",
         "--run_testcases", args.TestCaseRegex, "--circles"],
        cwd=hap_exec, env_vars=hap_env,
        error_message="Hapray 执行失败", live_output=True)

    # 5.1 处理 Hapray 报告：固定输出到 run_dir 根（多用例将覆盖并提示）
    print("\n=== 处理 Hapray 报告 ===")
    ensure_exists(reports_root, "Hapray 报告目录")

    ts_dirs = sorted([d for d in os.listdir(reports_root)
                      if d.isdigit() and len(d) == 14
                      and os.path.isdir(os.path.join(reports_root, d))],
                     reverse=True)
    if not ts_dirs:
        sys.exit("报告目录为空")
    ts_dir = os.path.join(reports_root, ts_dirs[0])
    print(f"使用报告目录: {ts_dir}")

    case_dirs = [d for d in os.listdir(ts_dir)
                 if "_round" not in d and os.path.isdir(os.path.join(ts_dir, d))]
    if not case_dirs:
        sys.exit("未找到用例文件夹")

    rep_dst_fixed = os.path.join(run_dir, "hapray_report.json")
    comp_dst_fixed = os.path.join(run_dir, "component_timings.json")

    for case in case_dirs:
        src_case = os.path.join(ts_dir, case)

        # hapray_report.json → 固定名
        rep_src = os.path.join(src_case, "report", "hapray_report.json")
        if os.path.exists(rep_src):
            if os.path.exists(rep_dst_fixed):
                print(f"⚠ 将覆盖 hapray_report.json（来自 {case}）")
            shutil.copy(rep_src, rep_dst_fixed)
            print(f"[{case}] √ hapray_report.json")
        else:
            print(f"[{case}] × 缺少 hapray_report.json")

        # hiperf_info.json → component_timings.json（提取 'har' 并按 count 降序）
        info_src = os.path.join(src_case, "hiperf", "hiperf_info.json")
        if not os.path.exists(info_src):
            print(f"[{case}] × 缺少 hiperf_info.json")
            continue

        try:
            with open(info_src, "r", encoding="utf-8") as f:
                info_obj: Any = json.load(f)
        except Exception as err:
            print(f"[{case}] × 解析 hiperf_info.json 失败: {err}")
            continue

        har_items: list[dict] = []
        if isinstance(info_obj, list):
            for blk in info_obj:
                if isinstance(blk, dict) and isinstance(blk.get("har"), list):
                    har_items.extend(blk["har"])
        elif isinstance(info_obj, dict) and isinstance(info_obj.get("har"), list):
            har_items = info_obj["har"]

        if not har_items:
            print(f"[{case}] × 未发现 'har' 数据")
            continue

        har_items.sort(key=lambda x: x.get("count", 0), reverse=True)

        try:
            if os.path.exists(comp_dst_fixed):
                print(f"⚠ 将覆盖 component_timings.json（来自 {case}）")
            with open(comp_dst_fixed, "w", encoding="utf-8") as f:
                json.dump(har_items, f, indent=2, ensure_ascii=False)
            print(f"[{case}] √ component_timings.json（{len(har_items)} 条）")
        except Exception as err:
            print(f"[{case}] × 写入 component_timings.json 失败: {err}")

    # 6) 结果提示
    print("\n=== 本次产出 ===")
    for fname in ("fileDepGraph.json", "moduleDepGraph.json",
                  "hapray_report.json", "component_timings.json"):
        fpath = os.path.join(run_dir, fname)
        print(("  √ " if os.path.exists(fpath) else "  × ") + f"{fname} -> {fpath}")

    print("\n=== 脚本执行完毕 ===")
    print("下一步（构建可视化输入）：")
    print(f"  python scripts/build/build_hierarchical_json.py --input-dir {run_dir} "
          f"--output web/hierarchical_integrated_data.json")


if __name__ == "__main__":
    main()
